package com.bcx.wind.workflow.imp.cmd.repositorycmd.processconfigcmd;

import com.bcx.wind.workflow.access.QueryFilter;
import com.bcx.wind.workflow.core.flow.NodeModel;
import com.bcx.wind.workflow.core.flow.node.ProcessModel;
import com.bcx.wind.workflow.entity.WindProcess;
import com.bcx.wind.workflow.entity.WindProcessConfig;
import com.bcx.wind.workflow.errorcontext.WindError;
import com.bcx.wind.workflow.interceptor.Command;
import com.bcx.wind.workflow.interceptor.CommandContext;
import com.bcx.wind.workflow.pojo.Condition;
import com.bcx.wind.workflow.support.Assert;
import com.bcx.wind.workflow.support.JsonHelper;
import com.bcx.wind.workflow.support.ObjectHelper;
import com.bcx.wind.workflow.support.TimeHelper;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import static com.bcx.wind.workflow.core.constant.Constant.ARRAY_NULL;
import static com.bcx.wind.workflow.core.constant.Constant.JSON_NULL;
import static com.bcx.wind.workflow.core.constant.ProcessStatus.RELEASE;
import static com.bcx.wind.workflow.message.ErrorCode.*;

/**
 * 添加更新实施配置数据命令
 *
 * @author zhanglei
 */
public class UpdateProcessConfigCommand implements Command<Integer> {

    private static final String MODULE = "processConfig";

    private static final String PROCESS_ID = "processId";

    private static final String PROCESS_NAME = "processName";

    private static final String NODE_ID = "nodeId";

    /**
     * 配置数据相关流程定义
     */
    private WindProcess windProcess;

    /**
     * 实施配置
     */
    private WindProcessConfig windProcessConfig;

    /**
     * 操作  1添加  2更新  默认添加
     */
    private int  operate = 1;

    /**
     * 执行数据
     */
    private CommandContext commandContext;

    public UpdateProcessConfigCommand(WindProcessConfig windProcessConfig,int operate){
        this.windProcessConfig = windProcessConfig;
        this.operate = operate;
    }

    @Override
    public Integer executor(CommandContext context) {
        try{
            this.commandContext = context;
            return this.execute();
        }catch (Exception e){
            commandContext.log(e);
            return null;
        }
    }

    private Integer  execute(){
        //校验数据
        checkConfigData();
        //获取排序
        int sort = getSort();
        //更新
        int ret =  addOrUpdate(sort);
        //添加缓存
        addCache();
        return ret;
    }

    /**
     * 添加到缓存中
     */
    private void addCache(){
        this.commandContext.getConfiguration().addProcessConfigCache(this.windProcessConfig.getProcessId(),
                          this.windProcessConfig);
    }

    private int addOrUpdate(int sort){
        this.windProcessConfig.setSort(sort);
        int ret = 0;
        if(this.operate == 1){
            ret =  this.commandContext.access().insertProcessConfig(this.windProcessConfig);
        }else{
            ret =  this.commandContext.access().updateProcessConfig(this.windProcessConfig);
        }
        if(ret != 1){
            WindError.error(DATABASE_OPERATE_ERROR,null);
        }
        return ret;
    }



    private int  getSort(){
        QueryFilter filter = new QueryFilter()
                .setProcessId(this.windProcessConfig.getProcessId())
                .setNodeId(this.windProcessConfig.getNodeId());
        List<WindProcessConfig> configList = this.commandContext.access().selectProcessConfigList(filter);
        return changeSort(configList);
    }


    /**
     * 校验配置数据是否合法
     */
    private  void checkConfigData(){
        if(this.windProcessConfig  == null){
            WindError.error(PROCESS_CONFIG_IS_NOT_NULL,null);
        }
        //校验设置非空数据
        checkNotEmpty();
        //校验数据是否正确
        checkData();
    }

    /**
     * 校验配置数据是否合法
     */
    @SuppressWarnings("unchecked")
    private void checkData(){
        if(this.windProcessConfig.getCondition() != null){
            try {
                List<Condition> conditions = JsonHelper.parseJson(this.windProcessConfig.getCondition().trim(), List.class, Condition.class);
                this.windProcessConfig.setCondition(ARRAY_NULL);
                for(Condition condition : conditions){
                    if(!ObjectHelper.isEmpty(condition)){
                        this.windProcessConfig.addCondition(condition);
                    }
                }
            }catch (Exception e){
                WindError.error(DATA_ERROR,e,MODULE,"condition");
            }
        }

        if(this.windProcessConfig.getNodeConfig() != null){
            try {
                JsonHelper.jsonToMap(this.windProcessConfig.getNodeConfig());
            }catch (Exception e){
                WindError.error(DATA_ERROR,e,MODULE,"nodeConfig");
            }
        }

        if(this.windProcessConfig.getBusinessConfig() != null){
            try {
                JsonHelper.jsonToMap(this.windProcessConfig.getBusinessConfig());
            }catch (Exception e){
                WindError.error(DATA_ERROR,e,MODULE,"businessConfig");
            }
        }
    }



    /**
     * 校验指定属性不可为空
     */
    private void checkNotEmpty(){
        if(this.operate == 2){
            String configId = this.windProcessConfig.getId();
            if(configId == null){
               WindError.error(UPDATE_OPERATE_LACK_PRIMARY,null,MODULE);
            }
            WindProcessConfig config = this.commandContext.access().getProcessConfigById(configId);
            if(config == null){
                WindError.error(QUERY_EMPTY,null,MODULE,configId);
            }
        }
        Assert.notEmptyError(LACK_DATA,this.windProcessConfig.getProcessId(),MODULE,PROCESS_ID);
        Assert.notEmptyError(LACK_DATA,this.windProcessConfig.getProcessName(),MODULE,PROCESS_NAME);
        Assert.notEmptyError(LACK_DATA,this.windProcessConfig.getNodeId(),MODULE,NODE_ID);

        //如果配置名称为空，默认使用节点名称
        if(this.windProcessConfig.getConfigName() == null){
            this.windProcessConfig.setConfigName(this.windProcessConfig.getNodeId());
        }
        if(this.operate == 1){
            this.windProcessConfig.setCreateTime(TimeHelper.nowDate());
        }
        //设置默认数据
        setDefaultValue();
        //设置等级
        setLevel();
    }

    /**
     * 设置配置等级
     */
    private void  setLevel(){
        //校验流程定义是否存在
        this.windProcess = this.commandContext.repositoryService().queryById(this.windProcessConfig.getProcessId());
        if(this.windProcess == null){
            WindError.error(PROCESS_QUERY_NULL,null,this.windProcessConfig.getProcessId());
        }
        //校验是否可以更新或者添加
        if(!RELEASE.equals(windProcess.getStatus())){
            WindError.error(ADD_CONFIG_NO_SUPPORT,null);
        }
        //节点ID为流程名称 等级为1
        if(windProcessConfig.getNodeId().equals(windProcess.getProcessName())){
            windProcessConfig.setLevel(1);
            return;
        }
        //校验节点名称是否存在
        checkNodeIdExist();
        //否则等级为2
        windProcessConfig.setLevel(2);
    }

    private  void checkNodeIdExist(){
        String nodeId = this.windProcessConfig.getNodeId();
        WindProcess process = this.commandContext.getConfiguration().getProcess(this.windProcessConfig.getProcessId());
        if(process != null){
            ProcessModel model = process.processModel();
            if(model != null){
                List<NodeModel> nodeModels =  model.getAllTaskNodes();
                nodeModels = nodeModels.stream().filter(node->node.nodeId().equals(nodeId))
                        .collect(Collectors.toList());
                if(ObjectHelper.isEmpty(nodeModels)){
                    WindError.error(NODE_NOT_FOUND,null,this.windProcess.getProcessName(),nodeId);
                }
            }
        }
    }



    /**
     * 设置默认值
     */
    private  void  setDefaultValue(){
        if(this.windProcessConfig.getCondition() == null){
            this.windProcessConfig.setCondition(ARRAY_NULL);
        }
        if(this.windProcessConfig.getNodeConfig() == null){
            this.windProcessConfig.setNodeConfig(JSON_NULL);
        }
        if(this.windProcessConfig.getApproveUser() == null){
            this.windProcessConfig.setApproveUser(ARRAY_NULL);
        }
        if(this.windProcessConfig.getBusinessConfig() == null){
            this.windProcessConfig.setBusinessConfig(JSON_NULL);
        }
    }

    /**
     * 设置排序
     * @param configList  节点下所有配置
     * @return            排序
     */
    private Integer changeSort(List<WindProcessConfig> configList){
        //设置排序初始值
        int maxSort = 1;

        //如果节点下配置为空， 则表示第一个新增配置,直接返回
        if(ObjectHelper.isEmpty(configList)){
            return maxSort;
        }

        //获取节点下配置集合中排序最高的
        maxSort = getMaxSort(configList);

        //如果操作为添加 排序+1  直接返回
        if(operate==1){
            return maxSort+1;
        }

        //更新  获取需要更新的配置排序  更新前排序
        int start = getStartConfigSort(configList,this.windProcessConfig);
        //如果排序字段为空则不修改
        if(ObjectHelper.isEmpty(this.windProcessConfig.getSort()) || this.windProcessConfig.getSort()==0){
            return start;
        }

        //更新后的排序
        int end = this.windProcessConfig.getSort();

        //如果更新后的排序大于最大排序，则为最大排序。如果小于1 则为1
        end = end > maxSort ? maxSort : end;
        end = end < 1 ? 1 : end;

        //如果更新后和更新前相等，则返回
        if(end==start){
            return end;
        }

        //获得单个操作指向
        int add = start > end ? 1 : -1;

        //获取需要更新的实施配置ID集合
        List<String> configIds = new LinkedList<>();
        for(WindProcessConfig con : configList){
            int sort = con.getSort();

            //如果 指向为1 表示从下往上更新排序,需要将开始上面结束下面包含结束的配置数据排序下移
            if(add==1 && sort<start && sort>=end){
                configIds.add(con.getId());
            }

            if(add==-1 && sort>start && sort<=end){
                configIds.add(con.getId());
            }

        }

        //更新sort
        this.commandContext.access().updateSort(configIds,add);
        return end;
    }

    private Integer  getStartConfigSort(List<WindProcessConfig> configList, WindProcessConfig config){
        return configList.stream().filter(c->c.getId().equals(config.getId()))
                .findFirst().map(WindProcessConfig::getSort)
                .orElseThrow(()->WindError.error("processConfig is not Exist"));
    }


    private Integer  getMaxSort(List<WindProcessConfig> configList){
        return configList.stream().max(Comparator.comparingInt(WindProcessConfig::getLevel))
                .map(WindProcessConfig::getSort)
                .orElseThrow(()->WindError.error("get max sort from configList fail! because data is error"));
    }
}
